package com.blandware.atleap.webapp.menu;

import com.blandware.atleap.common.util.ConvertUtil;
import org.apache.commons.lang.StringUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * <p>This class extends {@link MenuBase} and basically contains helper methods
 * for adding and fetching children and parents.</p>
 * <p>This class was copied from Struts Menu.</p>
 * <p><a href="MenuBase.java.html"><i>View Source</i></a></p>
 *
 * @author Scott Sayles
 * @author Matt Raible
 * @version $Revision: 1.10 $ $Date: 2008/02/19 12:55:39 $
 */
public class MenuComponent extends MenuBase implements Serializable {
	//~ Static fields/initializers =============================================
	protected static MenuComponent[] _menuComponent = new MenuComponent[0];

	//~ Instance fields ========================================================

	protected List menuComponents = new ArrayList();
	protected MenuComponent parentMenu = null;

    /**
     * Contains ID of this menu conponent
     */
    protected transient String ident = null;

	//~ Constructors ===========================================================

	/**
	 * Creates new MenuComponent
	 */
	public MenuComponent() {
		super();
	}


    //~ Methods ================================================================

    /**
     * Adds a menu component to list of children
     *
     * @param menuComponent Menu component to add
     */
	public void addMenuComponent(MenuComponent menuComponent) {
		if ( (menuComponent.getName() == null) ||
		        (menuComponent.getName().equals("")) ) {
			menuComponent.setName(this.name + menuComponents.size());
		}

		if ( !menuComponents.contains(menuComponent) ) {
			menuComponents.add(menuComponent);
			menuComponent.setParent(this);
		}
	}

    /**
     * Returns a list of child menu components
     *
     * @return List of children
     */
	public MenuComponent[] getMenuComponents() {
		MenuComponent[] menus =
		        (MenuComponent[]) menuComponents.toArray(_menuComponent);

		return menus;
	}

    /**
     * Sets a parent of this menu component
     *
     * @param parentMenu Menu component to set as parent
     */
	public void setParent(MenuComponent parentMenu) {
		if ( parentMenu != null ) {
			// look up the parent and make sure that it has this menu as a child
			if ( !parentMenu.getComponents().contains(this) ) {
				parentMenu.addMenuComponent(this);
			}
		}
		this.parentMenu = parentMenu;
	}

    /**
     * Returns a parent menu component
     *
     * @return Parent
     */
	public MenuComponent getParent() {
		return parentMenu;
	}

	/**
	 * Convenience method for Velocity templates
     *
     * @return list of child components
	 */
	public List getComponents() {
		return menuComponents;
	}

	/**
	 * Returns ID of this component. If ID has not already been initialized, generates it according to parent component's ID and
	 * position of this component in the list of parent's children. Can return <code>null</code> if there is no parent component of this one,
	 * or parent component's ID is <code>null</code>
	 *
	 * @return ID of this item
	 */
	public String getIdent() {
		if ( ident == null ) {
            if (parentMenu == null) {
                ident = "DropdownMenu__" + this.hashCode();
            } else {
                String parentId = parentMenu.getIdent();
                if ( parentId != null ) {
                    int pos = parentMenu.getComponents().indexOf(this);
                    ident = parentId + "__" + pos;
                }
            }
		}
		return ident;
	}

    /**
     * Returns list of all menu components in following order: root, his
     * first child descendants, his second child descendants, ... his last child
     * descendants
     *
     * @return list of items in preorder
     */
    public List getComponentsAsPlainList() {
        List result = new ArrayList();
        result.add(this);

        for (int i = 0; i < menuComponents.size(); i++) {
            MenuComponent child = (MenuComponent) menuComponents.get(i);
            result.addAll(child.getComponentsAsPlainList());
        }

        return result;
    }

    /**
     * Returns list of all parent components in following order: this
     * component, first parent, parent of parent ... root is excluded.
     * Method suggested by daimonion at wp dot pl.
     *
     * @return list of items in preorder
     */
    public List getMenuPath() {
        List result = new ArrayList();
        result.add(this);
        MenuComponent parent = this.getParent();

        while (parent != null) {
            if (parent.getParent() != null) result.add(parent);
            parent = parent.getParent();
        }

        Collections.reverse(result);
        return result;
    }

    /**
	 * Generates JavaScript code, which will create client-side representation of this server-side object
	 *
	 * @return JavaScript code, which will create client-side representation of this server-side object
	 */
	public String getJavascriptCode() {
		StringBuffer code = new StringBuffer("new DropdownMenuItem(");
		code.append("\"").append(getIdent()).append("\"");

		// append children one-by-one
		for ( int i = 0; i < menuComponents.size(); i++ ) {
			MenuComponent item = (MenuComponent) menuComponents.get(i);
			code.append(", ");
			if ( item != null ) {
				code.append(item.getJavascriptCode());
			} else {
				code.append(item);
			}
		}
		code.append(")");
		return code.toString();
	}

    /**
     * Returns comma-separated list of roles that are assigned to this component
     * or any of its ancestors
     *
     * @return list of roles
     */
    public String getRolesWithAncestors() {
        MenuComponent parent = getParent();
        if (parent == null) {
            return getRoles();
        } else {
            List thisRoles =  ConvertUtil.convertStringToList(getRoles(), ",", true);
            List parentRoles = ConvertUtil.convertStringToList(parent.getRolesWithAncestors(), ",", true);
            thisRoles.addAll(parentRoles);
            return ConvertUtil.convertListToString(thisRoles, ",");
        }
    }

    /**
	 * This method compares all attributes, except for parent and children
	 *
	 * @param o The object to compare to
	 * @return <code>true</code> if given object is equal to this 
	 */
	public boolean equals(Object o) {
		if ( !(o instanceof MenuComponent) ) {
			return false;
		}
		MenuComponent m = (MenuComponent) o;
		// Compare using StringUtils to avoid NullPointerExceptions
		return StringUtils.equals(m.getAction(), this.action) &&
		        StringUtils.equals(m.getAlign(), this.align) &&
		        StringUtils.equals(m.getAltImage(), this.altImage) &&
		        StringUtils.equals(m.getDescription(), this.description) &&
		        StringUtils.equals(m.getForward(), this.forward) &&
		        StringUtils.equals(m.getStyle(), this.style) &&
		        StringUtils.equals(m.getImage(), this.image) &&
		        StringUtils.equals(m.getLocation(), this.location) &&
		        StringUtils.equals(m.getName(), this.name) &&
		        StringUtils.equals(m.getOnclick(), this.onclick) &&
		        StringUtils.equals(m.getOnmouseout(), this.onmouseout) &&
		        StringUtils.equals(m.getOnmouseover(), this.onmouseover) &&
                StringUtils.equals(m.getHreflang(), this.hreflang) &&
                StringUtils.equals(m.getAnchor(), this.anchor) &&
		        StringUtils.equals(m.getRoles(), this.roles) &&
		        StringUtils.equals(m.getTarget(), this.target) &&
		        StringUtils.equals(m.getTitle(), this.title) &&
		        StringUtils.equals(m.getToolTip(), this.toolTip) &&
		        StringUtils.equals(m.getStyleClass(), this.styleClass);
	}
}
